﻿using FlyRemote.Base;
using System;
using System.Collections.Generic;
using System.Linq;

namespace FlyRemote.Remote
{
    /// <summary>
    /// 实现一个读取组的管理器 ，由此创建和管理读取组， 而不是管理读取组的 缓存数据
    ///  使用弱引用来管理 读取组，允许GC 回收
    ///  未回收的情况下， 返回已有的 读取组 
    ///  对字典进行了分段, 提高遍历速度 
    /// </summary>
    public static class ReadGroupManager
    {

        protected struct GroupMark
        {
            ulong BaseAddress;
            int RegionSize;

            public GroupMark(ulong address, int size)
            {
                BaseAddress = address;
                RegionSize = size;
            }
        }


        private static Dictionary<int, Dictionary<GroupMark, WeakReference>> Dict = new Dictionary<int, Dictionary<GroupMark, WeakReference>>();



        static ReadGroup makeGroup(int header, ref GroupMark mark, DataType type, UInt64 baseAddress, int regionSize, int? alignment, bool isModule = false)
        {

            var obj = new ReadGroup(type, baseAddress, regionSize, alignment == null ? 0 : (int)alignment, isModule);
            WeakReference wref = new WeakReference(obj);

            // 不包含 该地址头 
            if (!Dict.ContainsKey(header))
            {
                Dict[header] = new Dictionary<GroupMark, WeakReference>();
            }

            Dict[header][mark] = wref;

            return obj;
        }


        public static ReadGroup Create(DataType type, UInt64 baseAddress, int regionSize, int? alignment, bool isModule = false)
        {



            lock (Dict)
            {
                GroupMark mark = new GroupMark(baseAddress, regionSize);

                int hd2 = new ByteHeader2(baseAddress).header;
                hd2 &= 0xFFFF;

                // 不包含 该地址头 
                if (!Dict.ContainsKey(hd2))
                {
                    return makeGroup(hd2, ref mark, type, baseAddress, regionSize, alignment, isModule);
                }

                // 包含 该地址头 , 但该分组内无此记录 
                if (!Dict[hd2].ContainsKey(mark))
                {
                    return makeGroup(hd2, ref mark, type, baseAddress, regionSize, alignment, isModule);
                }


                // 有此记录,但是弱引用 已被回收 
                WeakReference wref = Dict[hd2][mark];
                if (wref.Target == null)
                {
                    return makeGroup(hd2, ref mark, type, baseAddress, regionSize, alignment, isModule);
                }


                // 引用有效 , 返回该引用的 对象 
                ReadGroup group = wref.Target as ReadGroup;

                if (type != DataType.Empty)
                    group.ElementDataType = type;

                if (alignment != null)
                    group.Alignment = (int)alignment;

                group.IsModuleRegion = isModule;

                return group;



            }
        }



        public static ReadGroup Create(UInt64 baseAddress, int regionSize)
        {
            return Create(DataType.Empty, baseAddress, regionSize, null, false);

        }


        /// <summary>
        /// 直接搜索 包含指定地址的 单个读取组 , 
        /// 避免  1.使用快照的方式 ,来查找读取组 (造成读取组无法释放 )
        ///       2.干扰到现在读取组中的信息 
        /// </summary>
        public static ReadGroup Search(ulong addr, int? size)
        {
            MemoryProtectionEnum requiredPageFlags = 0;
            MemoryProtectionEnum excludedPageFlags = 0;
            MemoryTypeEnum allowedTypeFlags = MemoryTypeEnum.None | MemoryTypeEnum.Private | MemoryTypeEnum.Image;

            NormalizedRegion region = null;

            for (int i = 0; i < 10; i++)
            {

                Int64 startAddress = (long)addr - 0x10000 * i;
                Int64 endAddress = (long)addr + 0x10000 * i;

                IEnumerable<NormalizedRegion> virtualPages = RMFactory.Default.GetVirtualPages(
                    requiredPageFlags,
                    excludedPageFlags,
                    allowedTypeFlags,
                    (ulong)startAddress,
                    (ulong)endAddress);

                if (virtualPages.Count() > 0)
                {
                    region = virtualPages.Where(o => o.BaseAddress <= addr && addr <= o.EndAddress).First();
                    break;
                }

            }

            if (region == null)
                return null;


            // 需要对内存段 切割  
            if (size != null && (int)size < region.RegionSize)
            {
                ulong baseAddress = 0;

                for (int i = 0; i < region.RegionSize / size; i++)
                {
                    baseAddress = region.BaseAddress + (uint)(size * i);
                    if (baseAddress <= addr && addr < baseAddress + (uint)size)
                    {
                        return Create(baseAddress, (int)size);
                    }
                }

            }
            else
            {

                return Create(region.BaseAddress, region.RegionSize);
            }


            return null;
        }
    }
}
